package org.alesv.entranceExams.web.taglib;


import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

import org.alesv.common.webapp.session.SessionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * Tag para el acceso desde las vistas a los atributos guardados en session. En el caso de que el atributo no se
 * encuentre en sesion, pintará dicho nombre precedido y sudecido por la caden "??".
 */
public class SessionTag extends TagSupport {

    /**
     * Identificador para la serialización.
     */
    private static final long   serialVersionUID = -1298968853552567982L;

    /**
     * El logger.
     */
    private static final Logger LOG              = LoggerFactory.getLogger(SessionTag.class);

    /**
     * Nombre del atributo que se quiere obtener de la sesión.
     */
    private String              attribute;

    /**
     * En caso del objeto a pintar sea iterable, se pintará como una lista.<br/>
     * Este parámetro determinará si se pinta como una lista ordenada o no Por defecto la lista no será ordenada.<br/>
     * En el caso de que el objeto a pintar no sea iterable, este parametro se ignorará.<br/>
     * Posibles valores: true o false
     */
    private String              orderedList;

    @Override
    public final int doStartTag() throws JspException {
        String result = "";
        HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
        HttpSession httpSession = request.getSession();
        SessionManager sessionManager = SessionManager.getSessionManager(httpSession);

        if (sessionManager == null) {
            result = "??" + attribute + "??";
        } else {
            StringTokenizer attributeTokenizer = new StringTokenizer(attribute, ".");

            String att = (String) attributeTokenizer.nextElement();
            Object o = sessionManager.getAttribute(att);

            try {
                while ((o != null) && attributeTokenizer.hasMoreElements()) {
                    att = (String) attributeTokenizer.nextElement();

                    if (o instanceof Map) {
                        // Si el objeto actual es un Map, accedemos directamente al siguiente objeto mediante su
                        // get("att")
                        o = ((Map) o).get(att);
                        if (o == null) {
                            LOG.warn("Intentando sacar de sesión " + attribute + " no se encontró " + att);
                        }
                    }
                    if (o instanceof List) {
                        // Si el objeto actual es un List, accedemos directamente al objeto mediante su
                        // get("num")
                        if (!((List) o).isEmpty()) {
                            o = ((List) o).get(Integer.parseInt(att));
                        } else {
                            o = null;
                        }
                        if (o == null) {
                            LOG.warn("Intentando sacar de sesión " + attribute + " no se encontró " + att);
                        }
                    } else {
                        // Si no, accederé por reflection al atributo deseado para tomarlo como objeto y seguir
                        // iterando.

                        Field[] declaredFields = o.getClass().getDeclaredFields();
                        Object fieldValue = null;
                        for (Field declaredField : declaredFields) {
                            declaredField.setAccessible(Boolean.TRUE);

                            if (declaredField.getName().equals(att)) {
                                fieldValue = declaredField.get(o);
                            }
                        }
                        o = fieldValue;
                        if (o == null) {
                            LOG.warn("Intentando sacar de sesión " + attribute + " no se encontró " + att);
                        }
                    }
                }
            } catch (IllegalArgumentException e) {
                LOG.info("IllegalArgumentException accediendo a " + attribute);
                o = null;
            } catch (IllegalAccessException e) {
                LOG.info("IllegalAccessException accediendo a " + attribute);
                o = null;
            }

            if (o != null) {
                // Antes de mostrarlo por la salida, nos aseguramos de que no es null
                if (o instanceof Iterable) {
                    // Si es un objeto sobre el que se puede iterar lo mostraremos como un listado.
                    StringBuilder listResult = new StringBuilder();

                    // En funcion del parametro de entrada orderedList, pintaremos una lista ordenada o no (ol o ul)
                    // Por defecto será una lista desordenada.
                    String listType = "ul";
                    if ("true".equalsIgnoreCase(orderedList)) {
                        listType = "ol";
                    }

                    listResult.append("<");
                    listResult.append(listType);
                    listResult.append(">");
                    for (Object item : (Iterable) o) {
                        listResult.append("<li>");
                        listResult.append(item.toString());
                        listResult.append("</li>");
                    }
                    listResult.append("</");
                    listResult.append(listType);
                    listResult.append(">");
                    result = listResult.toString();
                } else {
                    // Si no es iterable, simplemente lo mostramos.
                    result = o.toString();
                }
            } else {
                // Si el objeto es null, mostramos por la salida la cadena que se pasó como atributo con interrogaciones
                // antes y despues, para que se vea claramente que no se ha encontrado el atributo en sesión.
                result = "";
            }
        }

        try {
            pageContext.getOut().print(result);
        } catch (IOException e) {
            LOG.error("Escribiendo la salida");
        }

        return 0;
    }

    /**
     * Settea el valor de attribute.
     * 
     * @param attribute
     *            attribute.
     */
    public void setAttribute(String attribute) {
        this.attribute = attribute;
    }

    /**
     * @param orderedList
     *            the orderedList to set
     */
    public void setOrderedList(String orderedList) {
        this.orderedList = orderedList;
    }
}
